/*
 * xbee_svc.c
 *
 *  Created on: 29/10/2012
 *      Author: julian
 */

#include <stdio.h>
#include <errno.h>
#include <pthread.h>
#include <sys/msg.h>
#include <sys/time.h>

#include "xbee_svc.h"
#include "packet_input.h"
#include "msgq_utils.h"

static xbee_svc_handler_t _handler;
static xbee_svc_handler_t *_dev_handler = &_handler;

//--- member access methods

int xbeeGoodCount() {
    return _handler.mod.good;
}

int xbeeMACKedCount() {
    return _handler.mod.acked;
}
//--- worker thread functions

static void *receiverThr(void *arg) {

    xbee_svc_handler_t *devp = (xbee_svc_handler_t *) arg;

    while (devp->r_thr_active) {
        if (receiveFrames(&devp->mod, 0, FRAME_RCV_TIMEOUT_SEC) > 0) {
#ifdef XBEE_SVC_DEBUG
            printf("\nthr: good: %d", devp->mod.good);
#endif
        }
    }

    pthread_exit(NULL);
}

static void *senderThr(void * arg) {

    xbee_svc_handler_t *devp = (xbee_svc_handler_t *) arg;
    struct msgbuf_tx TX;

    if ((devp->mod.out_msqid = msgget((key_t) OUTPUT_QUEUE_KEY, 0)) == -1) {
#ifdef XBEE_SVC_DEBUG
        perror("senderThr:msgget.Error al enlazar con la cola");
#endif
        pthread_exit(NULL);
    }

    while (devp->s_thr_active) {
        if (msgrcv(devp->mod.out_msqid, (struct msgbuf *) &TX,
                sizeof(TX) - sizeof(long), 0, 0) == -1) {
            if (devp->s_thr_active) {
#ifdef XBEE_SVC_DEBUG
                perror("senderThr:msgrcv.Error al retirar el mensaje");
#endif
            }
            pthread_exit(NULL);
        } else {
            sendFrames(&devp->mod, TX);
        }
    }
    pthread_exit(NULL);
}

static void *processThr(void * arg) {
    xbee_svc_handler_t *devp = (xbee_svc_handler_t *) arg;
    struct msgbuf_rx RX;
    if ((devp->mod.in_msqid = msgget((key_t) INPUT_QUEUE_KEY, 0)) == -1) {
#ifdef XBEE_SVC_DEBUG
        perror("processThr:msgget.Error al enlazar con la cola");
#endif
        pthread_exit(NULL);
    }

    while (devp->pro_thr_active) {
        if (msgrcv(devp->mod.in_msqid, (struct msgbuf *) &RX,
                sizeof(RX) - sizeof(long), 0, 0) == -1) {
            if (devp->pro_thr_active) {
#ifdef XBEE_SVC_DEBUG
               perror("processThr:msgget.Error al recibir el mensaje");
#endif
            }
            pthread_exit(NULL);
        } else {
            packetInput(&RX.trama);
        }
    }
    pthread_exit(NULL);
}

//--- service init and shutdown

int xbeeSvcInit() {

    _dev_handler->s_thr_active = 1;
    _dev_handler->r_thr_active = 1;
    _dev_handler->pro_thr_active = 1;
    pthread_t r_thread = 0, s_thread = 0, proc_thread = 0;
    _dev_handler->r_thr = r_thread;
    _dev_handler->s_thr = s_thread;
    _dev_handler->proc_thr = proc_thread;

    if (xbeeDriverInit(SERIAL_PORT, 9600, &_dev_handler->mod) == -1) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSvcInit:xbeeDriverInit.Error inicializando el driver");
#endif
        return -1;
    }

    if (pthread_create(&(_dev_handler->r_thr), NULL, receiverThr, _dev_handler) < 0) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSvcInit:pthread_create.Error creando thread para recepción");
#endif
        return -1;
    }

    if (pthread_create(&(_dev_handler->s_thr), NULL, senderThr, _dev_handler) < 0) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSvcInit:pthread_create.Error creando thread para envío");
#endif
        return -1;
    }

    if (pthread_create(&(_dev_handler->proc_thr), NULL, processThr,_dev_handler) < 0) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSvcInit:pthread_create.Error creando thread de procesamiento");
#endif
        return -1;
    }

    return 0;
}

int xbeeSvcExit() {

    _dev_handler->s_thr_active = 0;
    _dev_handler->r_thr_active = 0;
    _dev_handler->pro_thr_active = 0;

    if (xbeeDriverExit(&_dev_handler->mod) == -1) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSvcExit:xbeeDriverExit.Error finalizando el driver");
#endif
        return -1;
    }
    if (pthread_join(_dev_handler->s_thr, NULL) < 0) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSvcExit:pthread_join.Error uniendo thread de envío");
#endif
        return -1;
    }
    if (pthread_join(_dev_handler->r_thr, NULL) < 0) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSvcExit:pthread_join.Error uniendo thread de recepción");
#endif
        return -1;
    }
    if (pthread_join(_dev_handler->proc_thr, NULL) < 0) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSvcExit:pthread_join.Error uniendo thread de procesamiento");
#endif
        return -1;
    }

#ifdef XBEE_SVC_DEBUG
        printf("\nthreads ended");
#endif

    _dev_handler = NULL;
    return 0;

}

//--- generic packet exchange functions

int xbeeSend(struct msgbuf_tx *qbuf) {
#ifdef XBEE_SVC_DEBUG
    print_tbuf(*qbuf);
#endif
    if (msgsnd(_dev_handler->mod.out_msqid, qbuf,
            sizeof(struct msgbuf_tx) - sizeof(long), IPC_NOWAIT) == -1) {
#ifdef XBEE_SVC_DEBUG
        perror("xbeeSend:msgsnd.Error al enviar mensaje");
#endif
        return -1;
    }
    return 0;
}

